package com.zhigh.project.security.jwt.web.configurers;

import com.zhigh.project.security.jwt.web.authentication.JWTAuthenticationFilter;
import com.zhigh.project.security.jwt.web.token.TokenExtractor;
import com.zhigh.project.security.jwt.web.token.BearerTokenExtractor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

public class JWTAuthorizationHeaderConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<JWTAuthorizationHeaderConfigurer<H>, H> {

    private TokenExtractor tokenExtractor = new BearerTokenExtractor();

    private RequestMatcher requiresAuthenticationRequestMatcher = new RequestHeaderRequestMatcher("Authorization");

    private boolean continueChainAfterSuccessfulAuthentication = true;

    private AuthenticationSuccessHandler successHandler = null;

    private AuthenticationFailureHandler failureHandler = null;

    @Override
    public void init(H builder) throws Exception {

    }

    @Override
    public void configure(H http) throws Exception {
        JWTAuthenticationFilter authFilter = createFilter(http);
        http.addFilterBefore(authFilter, LogoutFilter.class);
    }

    private JWTAuthenticationFilter createFilter(H http) {
        JWTAuthenticationFilter authenticationFilter = new JWTAuthenticationFilter(http.getSharedObject(AuthenticationManager.class));

        authenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));

        if (tokenExtractor != null) {
            authenticationFilter.setTokenExtractor(tokenExtractor);
        }

        if (requiresAuthenticationRequestMatcher != null) {
            authenticationFilter.setRequiresAuthenticationRequestMatcher(requiresAuthenticationRequestMatcher);
        }

        authenticationFilter.setContinueChainAfterSuccessfulAuthentication(continueChainAfterSuccessfulAuthentication);

        if (successHandler != null) authenticationFilter.setSuccessHandler(successHandler);

        if (failureHandler != null) authenticationFilter.setFailureHandler(failureHandler);

        return postProcess(authenticationFilter);

    }

    public JWTAuthorizationHeaderConfigurer<H> tokenExtractor(TokenExtractor tokenExtractor) {
        this.tokenExtractor = tokenExtractor;
        return this;
    }

    public JWTAuthorizationHeaderConfigurer<H> requiresAuthenticationRequestMatcher(RequestMatcher requiresAuthenticationRequestMatcher) {
        this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;
        return this;
    }

    public JWTAuthorizationHeaderConfigurer<H> enableContinueChainAfterSuccessfulAuthentication() {
        this.continueChainAfterSuccessfulAuthentication = true;
        return this;
    }

    public JWTAuthorizationHeaderConfigurer<H> disableContinueChainAfterSuccessfulAuthentication() {
        this.continueChainAfterSuccessfulAuthentication = false;
        return this;
    }


    public JWTAuthorizationHeaderConfigurer<H> successHandler(AuthenticationSuccessHandler successHandler) {
        this.successHandler = successHandler;
        return this;
    }

    public JWTAuthorizationHeaderConfigurer<H> failureHandler(AuthenticationFailureHandler failureHandler) {
        this.failureHandler = failureHandler;
        return this;
    }
}
